/*

** Splint - annotation-assisted static program checker

** Copyright (C) 1994-2003 University of Virginia,

**         Massachusetts Institute of Technology

**

** This program is free software; you can redistribute it and/or modify it

** under the terms of the GNU General Public License as published by the

** Free Software Foundation; either version 2 of the License, or (at your

** option) any later version.

** 

** This program is distributed in the hope that it will be useful, but

** WITHOUT ANY WARRANTY; without even the implied warranty of

** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

** General Public License for more details.

** 

** The GNU General Public License is available from http://www.gnu.org/ or

** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,

** MA 02111-1307, USA.

**

** For information on splint: info@splint.org

** To report a bug: splint-bug@splint.org

** For more information: http://www.splint.org

*/

/*

** osd.c

**

** Provide a system-independent interface to system-dependent

** file operations.

*/



/*

 * Modified by Herbert 04/19/97:

 * - added conditional 'OS2' to conditional 'MSDOS'

 * - added include of new header portab.h.

 * - changed '/' to macro.

 * - added DOS / OS/2 specific stuff in osd_getPath.

 * Herbert 06/12/2000:

 * - added OS/2 specific includes before osd_getPid()

 * - handle files like in WIN32 for OS/2 in osd_fileExists()

 * Herbert 02/17/2002:

 * - added OS/2 support to absolute file names

 */



/*@-allmacros*/

/*@ignore@*/

# include <sys/types.h>

# include <sys/stat.h>

/* Fix suggested by Lars Rasmussen */

# include <errno.h>



/* POSIX platforms should defined getpid in unistd.h */

# if defined (WIN32) || (defined(OS2) && defined(__IBMC__))

# include <process.h>

# include <direct.h>

# define getcwd _getcwd

# else

# include <unistd.h>

# endif



/*@end@*/

/*@=allmacros*/

# include "splintMacros.nf"

# include "basic.h"

# include "osd.h"



/* from stat.h */

/*@ignore@*/

extern int stat (const char *, /*@out@*/ struct stat *);

/*@end@*/



static bool osd_executableFileExists (char *);



static bool nextdir (char **p_current_dir, /*@out@*/ char **p_dir, 

		     /*@out@*/ size_t *p_len);



extern cstring LSLRootName (cstring filespec)

{

  /*@access cstring@*/

  char *result, *startName, *tail;

  size_t nameLength;



  llassert (cstring_isDefined (filespec));

  tail = strrchr (filespec, CONNECTCHAR);

  startName = (tail == NULL ? filespec : &tail[1]);

  tail = strrchr (startName, '.');

  nameLength = (tail == NULL ? strlen (startName) 

		: size_fromInt (tail - startName));

  result = dmalloc (nameLength + 1);

  strncpy (result, startName, nameLength);

  result[(int) nameLength] = '\0';

  return result;

  /*@noaccess cstring@*/

}



extern /*@observer@*/ cstring

osd_getEnvironment (cstring env, /*@returned@*/ cstring def)

{

  /*@access cstring@*/

  char *ret = osd_getEnvironmentVariable (env);



  if (ret == NULL)

    {

      return def;

    }

  else

    {

      return ret;

    }

  /*@noaccess cstring@*/

}





/*

**++

**  FUNCTIONAL DESCRIPTION:

**

**      This function attempts to locate a file in a search list.  On VMS, it

**	just concatinates the path and file names, and then lets RMS do the

**	searching.  On Ultrix, it searches for the file on the path.

**

**  FORMAL PARAMETERS:

**

**      path:	    search path where to look for the file.

**	file:	    name of file to search for.

**	returnPath: if a file is found, this is where the concatenated

**		    directory and file name are returned.

**

**  RETURN VALUE:

**

**      OSD_FILEFOUND:	    the file was found on the search list.

**	OSD_FILENOTFOUND    the file was not found.

**	OSD_PATHTOOLONG	    the concatenated directory and file name are too

**			    long.

**

**  SIDE EFFECTS:

**

**      None

**

**  PRECONDITIONS:

**

**  	Requires that parameters, path and file, are valid C strings.

**

**

**--

*/



extern /*@observer@*/ cstring osd_getHomeDir ()

{

  /* Would something different be better for windows? */

  return (osd_getEnvironmentVariable (cstring_makeLiteralTemp ("HOME")));

}



filestatus osd_findOnLarchPath (cstring file, cstring *returnPath)

{

  return (osd_getPath (context_getLarchPath (), file, returnPath));

}



extern filestatus

osd_getPath (cstring path, cstring file, cstring *returnPath)

{

  char *fullPath;

  char *dirPtr;

  size_t dirLen;

  char aPath[MAXPATHLEN];

  filestatus rVal = OSD_FILENOTFOUND;	/* assume file not found. */

  

  /*@access cstring@*/

  

  fullPath = path;

  llassert (cstring_isDefined (file));

  

  /* 2002-01-01: make sure returnPath gets defined even when there are errors.

  **             (fixed splint checking detected this)

  */



  *returnPath = cstring_undefined;



  if (fullPath == NULL 

      || 

# if defined(OS2) || defined(MSDOS) || defined(WIN32)

      /* under OS/2 and MSDOS the includePath may be empty, if so, search 

       * the current directory. */

      *fullPath == '\0' || 

      (*file == CONNECTCHAR || (file[0] != '\0' && file[1] == ':'))

# else

      (*file == CONNECTCHAR)

# endif

      )

    {

      /* No path specified. Look for it in the current directory.	    */

      

      strcpy (&aPath[0], file);

      

      if (osd_fileExists (&aPath[0]))

	{

	  rVal = OSD_FILEFOUND;

   	  *returnPath = dmalloc (strlen (&aPath[0]) + 1);

	  strcpy (*returnPath, &aPath[0]);

	}

    }

  else

    {

      /* Path specified. Loop through directories in path looking for the */

      /* first occurrence of the file.				    */

      

      while (nextdir (&fullPath, &dirPtr, &dirLen) 

	     && rVal == OSD_FILENOTFOUND)

	{

	  if ((dirLen + strlen (file) + 2) <= MAXPATHLEN)

	    {

	      /* Cat directory and filename, and see if file exists.  */

	      strncpy (&aPath[0], dirPtr, dirLen);

	      strcpy (&aPath[0] + dirLen, "");	/* Null terminate aPath. */

	      strcat (&aPath[0], CONNECTSTR);

	      strcat (&aPath[0], file);

	      

	      if (osd_fileExists (&aPath[0]))

		{

		  rVal = OSD_FILEFOUND;

   		  *returnPath = (char *) dmalloc (strlen (&aPath[0]) + 1);

		  strcpy (*returnPath, &aPath[0]);

		}

	    }

	  else

	    {

	      rVal = OSD_PATHTOOLONG;

	    }

	}	

    }



  return rVal;

  /*@noaccess cstring@*/

}



extern filestatus

osd_getExePath (cstring path, cstring file, cstring *returnPath)

{

  char *fullPath;

  char *dirPtr;

  size_t dirLen;

  char aPath[MAXPATHLEN];

  filestatus rVal = OSD_FILENOTFOUND;	/* assume file not found. */

  /*@access cstring@*/ 



  *returnPath = cstring_undefined;  

  fullPath = osd_getEnvironmentVariable (path);

  

  if (fullPath == NULL)

    {

      /* No path specified. Look for it in the current directory. */

      llassert (cstring_isDefined (file));

      strcpy (&aPath[0], file);

      

      if (osd_fileExists (&aPath[0]))

	{

	  rVal = OSD_FILEFOUND;

   	  *returnPath = dmalloc (strlen (&aPath[0]) + 1);

	  strcpy (*returnPath, &aPath[0]);

	}

    }

  else

    {

     /* 

     ** Path specified. Loop through directories in path looking

     ** for the first occurrence of the file.				    

     */



      while (nextdir (&fullPath, &dirPtr, &dirLen) &&

	     rVal == OSD_FILENOTFOUND)

	{

	  llassert (cstring_isDefined (file));



	  if ((dirLen + cstring_length (file) + 2) <= MAXPATHLEN)

	    {

	      /* Cat directory and filename, and see if file exists.  */

	      strncpy (&aPath[0], dirPtr, dirLen);

	      strcpy (&aPath[0] + dirLen, ""); /* Null terminate aPath. */

	      strcat (&aPath[0], CONNECTSTR);

	      strcat (&aPath[0], file);



	      if (osd_executableFileExists (&aPath[0]))

		{

		  rVal = OSD_FILEFOUND;

   		  *returnPath = dmalloc (strlen (&aPath[0]) + 1);

		  strcpy (*returnPath, &aPath[0]);

		}

	    }

	  else

	    {

	      rVal = OSD_PATHTOOLONG;

	    }

	}

    }



  return rVal;

  /*@noaccess cstring@*/

}



bool

osd_fileExists (cstring filespec)

{

# ifdef UNIX

  struct stat buf;

  /*@i3@*/ return (stat (cstring_toCharsSafe (filespec), &buf) == 0); /* spurious */

# else

# if defined (WIN32) || defined (OS2)

  FILE *test = fileTable_openReadFile (context_fileTable (), filespec);

  

  if (test != NULL) 

    {

      (void) fileTable_closeFile (context_fileTable (),test);

      return TRUE;

    } 

  else

    { 

      return FALSE;

    }

# else 

  return FALSE;

# endif

# endif

}



# if defined(__IBMC__) && defined(OS2)

# define S_IFMT (unsigned short)0xFFFF

# endif



/*

** Works form Win32 at least...

*/



# ifndef S_IXUSR

/*@-macrounrecog@*/

# define S_IXUSR _S_IEXEC

/*@=macrounrecog@*/

# endif



bool

osd_executableFileExists (/*@unused@*/ char *filespec)

{

  /*@-compdestroy@*/ /* possible memory leaks here? */

# ifdef UNIX

  struct stat buf;

  if (stat (filespec, &buf) == 0)

    { 

      /* mask by file type */

      /*@-type@*/ /* confusion about __mode_t and mode_t types */

      if ((buf.st_mode & S_IFMT) != S_IFDIR) /* not a directory */ 

	/*@=type@*/

	{

	  /* as long as it is an executable file */

# if defined(__IBMC__) && defined(OS2)

	  int com_or_exe_pos = strlen( filespec) - 4;

	  return stricmp( &filespec[com_or_exe_pos], ".exe") == 0

	    || stricmp( &filespec[com_or_exe_pos], ".com") == 0

	    || stricmp( &filespec[com_or_exe_pos], ".bat") == 0

	    || stricmp( &filespec[com_or_exe_pos], ".cmd") == 0;

# else

	  return (((buf.st_mode & S_IXUSR)

# if defined (S_IXGRP) && defined (S_IXOTH)

		   | (buf.st_mode & S_IXGRP) |

		   (buf.st_mode & S_IXOTH)

# endif

		   ) != 0); /* spurious */

# endif

	}

    }

# endif

  return (FALSE); 

  /*@=compdestroy@*/

}



/*

**++

**  FUNCTIONAL DESCRIPTION:

**

**      Find the next directory from a directory path.

**

**  FORMAL PARAMETERS:

**

**      char ** current_dir :

**	    Points to the current position in the path string.  The first time

**	    you call this routine, this should point to the first character of

**	    the path.  On return, this will be updated to point to the

**	    terminating \0 or : of the first directory found.  You can then pass

**	    it unchanged for subsequent calls; this routine will correctly skip

**	    over the :.

**

**	char ** dir :

**	    On exit, this will point to the first character of the directory

**	    that was found.  This will be a pointer directly into the client's

**	    path string.

**

**	unsigned int * len :

**	    On exit, this will contain the length of the directory that was

**	    found, not counting any terminating \0 or :.  If no directory was

**	    found, this will be 0.

**

**  RETURN VALUE:

**	TRUE if we found another directory.

**	FALSE otherwise.

**

**  DESIGN:

**

**      We return a pointer and length, rather than a string, because of a)

**	historical reasons; and b) this avoids having to allocate storage.

**

**

**

**--

*/



static bool

nextdir (d_char *current_dir, d_char *dir, size_t *len)

{

  char *tchar;



  if (**current_dir == '\0')

    {

      *len = 0;

      *dir = NULL;

      return FALSE;

    }



  *dir = (**current_dir == PATH_SEPARATOR ? *current_dir + 1 : *current_dir);

  

  /* Find next ':' or end of string */

  for (tchar = *dir; *tchar != '\0' && *tchar != PATH_SEPARATOR; tchar++)

    {

      ;

    }

  

  *current_dir = tchar;

  *len = size_fromInt (tchar - *dir);

  return TRUE;

}



/*@observer@*/ /*@null@*/ cstring osd_getEnvironmentVariable (cstring var)

{

  /* evans - 2001-08-26 fixed OS instead of OS2 bug, reported by Alexander Mai */

# if defined(UNIX) || defined(OS2) || defined(MSDOS) || defined(WIN32)

  char *val = getenv (cstring_toCharsSafe (var));



  if (val == NULL) 

    {

      return cstring_undefined;      

    } 

  else 

    {

      return cstring_makeLiteralTemp (val);

    }

# else

  return cstring_undefined;

# endif

}



# if defined (WIN32) || defined (OS2) && defined (__IBMC__)

extern /*@external@*/ int _flushall (void) /*@modifies fileSystem@*/ ;

# endif



# ifndef system

extern /*@external@*/ int system (const char *) /*@modifies fileSystem@*/ ;

# endif

int osd_system (cstring cmd)

{

  int res;

    /* system ("printenv"); */

# if defined (WIN32) || defined (OS2) && defined (__IBMC__)

  (void) _flushall (); 

# endif



  res = system (cstring_toCharsSafe (cmd));

  return res;

}



# ifndef unlink

/* This should be defined by unistd.h */

/*@-redecl@*/

extern /*@external@*/ int unlink (const char *) /*@modifies fileSystem@*/ ;

/*@=redecl@*/

# endif



static bool s_tempError = FALSE;



void osd_setTempError (void)

{

  s_tempError = TRUE;

}



int osd_unlink (cstring fname)

{

  int res;



  res = unlink (cstring_toCharsSafe (fname));



  if (res != 0)

    {

      if (!s_tempError)

	{

	  llcontbug (message ("Cannot remove temporary file: %s (%s)",

			      fname,

			      cstring_fromChars (strerror (errno))));

	}

    }

  

  return res;

}



# if defined (WIN32) || defined (OS2) && defined (__IBMC__)

int

# else

int /* pid_t */

# endif

osd_getPid ()

{

# if defined (WIN32) || defined (OS2) && defined (__IBMC__)

  int pid = _getpid ();

# else

  int pid = (int)getpid ();

# endif



  return pid;

}

  

cstring osd_fixDefine (cstring x)

{

  /*@access cstring@*/

  llassert (cstring_isDefined (x));

# ifdef UNIX

  if (strchr (x, '\'') != NULL) {

    /*

    ** If there is a single quote, check for <ident>='<string>' and 

    ** produce <ident>=<string>

    */



    char *eqs = strchr (x, '=');



    if (eqs != NULL) {

      if (eqs[1] == '\'') {

	char *endqu = strrchr (x, '\'');



	if (endqu != NULL) {

	  if (*(endqu - 1) != '\\') {

	    if (*(endqu + 1) == '\0') {

	      cstring res;

	      cstring def;



	      *endqu = '\0';

	      def = cstring_fromChars (eqs + 2);

	      eqs[1] = '\0';

	      res = cstring_concat (cstring_fromChars (x), def);

	      	      return res;

	    }

	  }

	}

      }

    }

  } 



# endif



  return cstring_copy (x);

  /*@noaccess cstring@*/

}



bool osd_fileIsReadable (cstring f)

{

  FILE *fl = fileTable_openReadFile (context_fileTable (), f);



  if (fl != NULL)

    {

      check (fileTable_closeFile (context_fileTable (), fl));

      return (TRUE);

    }

  else

    {

      return (FALSE);

    }

}



bool osd_isConnectChar (char c)

{

  if (c == CONNECTCHAR) 

    {

      return TRUE;

    }



# ifdef HASALTCONNECTCHAR

  if (c == ALTCONNECTCHAR)

    {

      return TRUE;

    }

# endif



  return FALSE;

}



/*

** Returns true if c2 starts with the same path as c1

**

** This is called by context_isSystemDir to determine if a

** directory is on the system path.

**

** In unix, this is just a string comparison.  For Win32 and OS2, we need a more

** complex comparison.

*/



bool osd_equalCanonicalPrefix (cstring dirpath, cstring prefixpath)

{

  llassert (cstring_isDefined (prefixpath));



  if (cstring_isEmpty (dirpath)) 

    {

      return (cstring_isEmpty (prefixpath));

    }



# if defined (WIN32) || defined (OS2)



  /*@access cstring@*/ /* Moved this from cstring - should abstract it... */



  /*

  ** If one has a drive specification, but the other doesn't, skip it.

  */

  

  if (strchr (dirpath, ':') == NULL

      && strchr (prefixpath, ':') != NULL)

    {

      prefixpath = strchr (prefixpath, ':') + 1;

    }

  else 

    {

      if (strchr (prefixpath, ':') == NULL

	  && strchr (dirpath, ':') != NULL)

	{

	  dirpath = strchr (dirpath, ':') + 1;

	}

    }



  {

    int len = size_toInt (strlen (prefixpath));

    int i = 0;

    int slen = 0;



    for (i = 0, slen = 0; i < len; i++, slen++)

      {

	/* Allow any number of connect characters in any combination:

	 * c:/usr//src\/foo == c:\\usr/src\/foo 

	 * After this we'll be at the last of such a sequence */



	if (osd_isConnectChar (dirpath[slen]) && osd_isConnectChar (prefixpath[i]))

	  {

	    /* Skip one or more connect chars */



	    for (; osd_isConnectChar (dirpath[slen+1]); ++slen)

	      {

		; 

	      }

	    

	    for (; osd_isConnectChar (prefixpath[i+1]); ++i)

	      {

		;

	      }

	  }

	/* Windows, MSDOS and OS/2 use case-insensitive path specs! */

	else if (toupper (dirpath[slen]) != toupper (prefixpath[i]))

	  {

	    return FALSE;

	  }



      }

  }



  /*@noaccess cstring@*/ 



  return TRUE;

# else

  return (cstring_equalPrefix (dirpath, prefixpath));

# endif

}



# if 0

/*

** This code provided by Herbert Martin Dietze, to canonicalize path names.

*/



char *osd_getcwd (/*@returned@*/ char *str, size_t size)

{ 

  return getcwd (str, size);

}



/*@null@*/ /*@observer@*/ char *

osd_dirNext (char *str)

{

  char *p1 = strchr (str, '/');

  char *p2 = strchr (str, '\\');



  if (p1 == NULL)

    {

      if (p2 != NULL)

	{

	  return p2 + 1;

	}

      else

	{

	  return NULL;

	}

    }

  else if (p2 == NULL)

    {

      return p1 + 1;

    }

  else /* both not null */

    {

      return (p1 < p2 ? p1 : p2) + 1;

    }

}



static void 

osd_dirShift (char *str, size_t num) /*@modifies str@*/

{

  int i;

  

  assert (num <= strlen (str));

  

  for (i = 0; str[i] != '\0'; i++)

    {

      str[i] = str[i + num];

    }

}



bool

osd_dirDotdot (char *str)

{

  return str[0] == '.' && str[1] == '.' && osd_isConnectChar (str[2]);

}



void

osd_dirNormalize (char *str)

{

  char *pos1, *pos2;



  for (; osd_isConnectChar (str[0]); str++)

    {

    }



  for (; str != NULL && osd_dirDotdot (str); str = osd_dirNext (str))

    {

    }



  for (pos1 = pos2 = str; 

       pos1 != NULL; 

       pos2 = pos1, pos1 = osd_dirNext (pos1))

    {

      /* remove redundant `./' entry */

      while (pos1[0] == '.' && osd_isConnectChar (pos1[1]))

        {

          osd_dirShift (pos1, 2);

        }



      /* remove redundant `foo/../' entry */

      if (osd_dirDotdot (pos1) && pos2 < pos1)

        {

          osd_dirShift (pos2, pos1 - pos2 + 1);

          osd_dirNormalize (str);

        }

    }

}



/*@null@*/ char *

osd_dirAbsolute (char *str)

{

  char *ret = NULL;

  size_t size = PATH_MAX * sizeof (*ret);

  

  DPRINTF (("Absolute for: %s", str));



# if defined (WIN32) || defined (OS2) || defined (MSDOS)

  if (strlen (str) > 1 && str[1] == ':')

    {

      /*

      ** Its a drive letter

      */

      

      ret = dmalloc ((strlen (str) + 1) * sizeof (*ret));

      strcpy (ret, str);

    }

  else

# endif

  if (osd_isConnectChar (str[0]))

    {

      ret = dmalloc ((strlen (str) + 1) * sizeof (*ret));

      strcpy (ret, str);

    }

  else

    {

      ret = dmalloc (size);

      

      ret = osd_getcwd (ret, size);

      ret = realloc (ret, (strlen (str) + strlen (ret) + 2) * sizeof (*ret));



      if (ret == NULL)

        {

          return NULL;

        }



      strcat (ret, CONNECTSTR);

      strcat (ret, str);

    }

  

  osd_dirNormalize (ret);

  return ret;

}



# endif



/*

** absolute paths

**

** This code is adapted from:

**

** http://src.openresources.com/debian/src/devel/HTML/S/altgcc_2.7.2.2.orig%20altgcc-2.7.2.2.orig%20protoize.c.html#1297

**

**

**  Protoize program - Original version by Ron Guilmette (rfg@segfault.us.com).

**   Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.

**

** This file is part of GNU CC.

**

** GNU CC is free software; you can redistribute it and/or modify

** it under the terms of the GNU General Public License as published by

** the Free Software Foundation; either version 2, or (at your option)

** any later version.

**

** GNU CC is distributed in the hope that it will be useful,

** but WITHOUT ANY WARRANTY; without even the implied warranty of

** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

** GNU General Public License for more details.

** 

** You should have received a copy of the GNU General Public License

** along with GNU CC; see the file COPYING.  If not, write to

** the Free Software Foundation, 59 Temple Place - Suite 330,

** Boston, MA 02111-1307, USA.  

*/



/* 

** Return the absolutized filename for the given relative

** filename.  Note that if that filename is already absolute, it may

** still be returned in a modified form because this routine also

** eliminates redundant slashes and single dots and eliminates double

** dots to get a shortest possible filename from the given input

** filename.  The absolutization of relative filenames is made by

** assuming that the given filename is to be taken as relative to

** the first argument (cwd) or to the current directory if cwd is

** NULL.  

*/



/* A pointer to the current directory filename (used by abspath).  */

static /*@only@*/ cstring osd_cwd = cstring_undefined;



static void osd_setWorkingDirectory (void)

{

# if defined (UNIX)

  char *buf = dmalloc (sizeof (*buf) * MAXPATHLEN);

  char *cwd = getcwd (buf, MAXPATHLEN);

#elif defined (OS2)

  char *buf = dmalloc (sizeof (*buf) * MAXPATHLEN);

  char *cwd = _getcwd2 (buf, MAXPATHLEN);

  char *slash;

  while ((slash = strchr (cwd, '/')) != NULL)

    {

      *slash = '\\';

    }

#endif

# if defined (UNIX) || defined (OS2)

  llassert (cstring_isUndefined (osd_cwd));



  if (cwd == NULL)

    {

      lldiagmsg (message ("Cannot get working directory: %s\n",

			  lldecodeerror (errno)));

      osd_cwd = cstring_makeLiteral ("<missing directory>");

    }

  else

    {

      osd_cwd = cstring_fromCharsNew (cwd);

    }



  sfree (buf);

# else

  ; /* Don't know how to do this for non-POSIX platforms */

# endif

}



void osd_initMod (void)

{

  osd_setWorkingDirectory ();

}



void osd_destroyMod (void)

{

  cstring_free (osd_cwd);

  osd_cwd = cstring_undefined;

}



cstring osd_absolutePath (cstring cwd, cstring filename)

{

# if defined (UNIX) || defined (OS2)

  /* Setup the current working directory as needed.  */

  cstring cwd2 = cstring_isDefined (cwd) ? cwd : osd_cwd;

  char *abs_buffer;

  char *endp, *outp, *inp;



  /*@access cstring@*/

  llassert (cstring_isDefined (cwd2));

  llassert (cstring_isDefined (filename));



  abs_buffer = (char *) dmalloc (cstring_length (cwd2) + cstring_length (filename) + 2);

  endp = abs_buffer;

  

  /*

  ** Copy the  filename (possibly preceded by the current working

  ** directory name) into the absolutization buffer.  

  */

  

  {

    const char *src_p;



    if (!osd_isConnectChar (filename[0])

# ifdef OS2

	&& !(isalpha (filename[0]) && filename[1] == ':')

# endif

	)

      {

        src_p = cwd2;



        while ((*endp++ = *src_p++) != '\0') 

	  {

	    continue;

	  }



        *(endp-1) = CONNECTCHAR;                        /* overwrite null */

      }



    src_p = filename;



    while ((*endp++ = *src_p++) != '\0')

      {

	continue;

      }

  }

  

  /* Now make a copy of abs_buffer into abs_buffer, shortening the

     filename (by taking out slashes and dots) as we go.  */

  

  outp = inp = abs_buffer;

  *outp++ = *inp++;             /* copy first slash */

#ifdef apollo

  if (inp[0] == '/')

    *outp++ = *inp++;           /* copy second slash */

#endif

  for (;;)

    {

      if (inp[0] == '\0')

	{

	  break;

	}

      else if (osd_isConnectChar (inp[0]) && osd_isConnectChar (outp[-1]))

	{

	  inp++;

	  continue;

	}

      else if (inp[0] == '.' && osd_isConnectChar (outp[-1]))

	{

	  if (inp[1] == '\0')

	    {

	      break;

	    }

	  else if (osd_isConnectChar (inp[1]))

	    {

	      inp += 2;

	      continue;

	    }

	  else if ((inp[1] == '.') 

		   && (inp[2] == '\0' || osd_isConnectChar (inp[2])))

	    {

	      inp += (osd_isConnectChar (inp[2])) ? 3 : 2;

	      outp -= 2;

	

	      while (outp >= abs_buffer && !osd_isConnectChar (*outp))

		{

		  outp--;

		}



	      if (outp < abs_buffer)

		{

		  /* Catch cases like /.. where we try to backup to a

                     point above the absolute root of the logical file

                     system.  */

		  

		  llfatalbug (message ("Invalid file name: %s", filename));

		}



	      *++outp = '\0';

	      continue;

	    }

	  else

	    {

	      ;

	    }

	}

      else

	{

	  ;

	}



      *outp++ = *inp++;

    }

  

  /* On exit, make sure that there is a trailing null, and make sure that

     the last character of the returned string is *not* a slash.  */

  

  *outp = '\0';

  if (osd_isConnectChar (outp[-1]))

    *--outp  = '\0';

  

  /*@noaccess cstring@*/

  return cstring_fromChars (abs_buffer);

# else

  DPRINTF (("Here: %s", filename));

  return cstring_copy (filename);

# endif

}



/* 

** Given a filename (and possibly a directory name from which the filename

** is relative) return a string which is the shortest possible

** equivalent for the corresponding full (absolutized) filename.  The

** shortest possible equivalent may be constructed by converting the

** absolutized filename to be a relative filename (i.e. relative to

** the actual current working directory).  However if a relative filename

** is longer, then the full absolute filename is returned.

**

** KNOWN BUG:   subpart of the original filename is actually a symbolic link.  

**

** this is really horrible code...surely someone has written a less buggy version of this!

*/



cstring osd_outputPath (cstring filename)

{

# if defined (UNIX) || defined (OS2)

  char *rel_buffer;

  char *rel_buf_p;

  cstring cwd_p = osd_cwd;

  char *path_p;

  int unmatched_slash_count = 0;

  size_t filename_len = cstring_length (filename);

  

  llassertretval (filename_len > 0, filename);



  /*@access cstring@*/

  path_p = filename;

  rel_buffer = (char *) dmalloc (filename_len);

  rel_buf_p = rel_buffer;

  *rel_buf_p = '\0';



  if (cwd_p == NULL) 

    {

      /* Need to prevent recursive assertion failures */

      return cstring_copy (filename);

    }



  llassert (cwd_p != NULL);

  llassert (path_p != NULL);



  while ((*cwd_p != '\0') && (*cwd_p == *path_p))

    {

      cwd_p++;

      path_p++;

    }

  

  if ((*cwd_p == '\0') && (*path_p == '\0' || osd_isConnectChar (*path_p)))  /* whole pwd matched */

    {

      if (*path_p == '\0')             /* input *is* the current path! */

	{

	  cstring_free (rel_buffer);

	  return cstring_makeLiteral (".");

	}

      else

	{

	  cstring_free (rel_buffer);

	  return cstring_fromCharsNew (path_p + 1);

	}

    }

  else

    {



      /* drl   2002-10/14 I had to put this code back*/

      /* the case that needs it is when splint is given an absolute path name of a file outside of the current directory and the subdirectories below the current directory. e.g. cd /home/; splint /tmp/prog.c

       */

      

      /* evans 2002-02-05 This is horrible code, which I've removed.  I couldn't find any

      ** test cases that need it, so I hope I'm not breaking anything.

      */

      /*#if 0*/



      if (*path_p != '\0')

        {

          --cwd_p;

          --path_p;



          while (cwd_p >= osd_cwd && !osd_isConnectChar (*cwd_p)) /* backup to last slash */

            {

              --cwd_p;

              --path_p;

            }

          cwd_p++;

          path_p++;

          unmatched_slash_count++;

        }



      /* Find out how many directory levels in cwd were *not* matched.  */

      while (*cwd_p != '\0')

	{

	  if (osd_isConnectChar (*cwd_p++))

	    unmatched_slash_count++;

	}

      

      /* Now we know how long the "short name" will be.

         Reject it if longer than the input.  */

      if (unmatched_slash_count * 3 + strlen (path_p) >= filename_len)

	{

	  cstring_free (rel_buffer);

	  /* fprintf (stderr, "Returning filename: %s [%p]\n", filename); */

	  return cstring_copy (filename);

	}



      /*drl 10-14-2002 end previously removed code */

      /*#endif*/

      /* For each of them, put a `../' at the beginning of the short name.  */

      while (unmatched_slash_count-- > 0)

        {

          /* Give up if the result gets to be longer

             than the absolute path name.  */

	  char * temp_rel_buf_p;



	  /*drl This comment is necessary because for some reason Splint

	    does not realize that the pasts where rel_buf_p is released

	    do not reach here*/

	  /*@-usereleased@*/

	  temp_rel_buf_p = rel_buf_p;

	  /*@-usereleased@*/

	  

          if (rel_buffer + filename_len <= temp_rel_buf_p + 3)

	    {

	      sfree (rel_buffer);

	      return cstring_copy (filename);

	    }



          *rel_buf_p++ = '.';

          *rel_buf_p++ = '.';

          *rel_buf_p++ = CONNECTCHAR;

        }

      

      /* Then tack on the unmatched part of the desired file's name.  */



      do

        {

          if (rel_buffer + filename_len <= rel_buf_p)

	    {

	      cstring_free (rel_buffer);

	      return cstring_copy (filename);

	    }

        } /*@-usereleased@*/

      while ((*rel_buf_p++ = *path_p++) != '\0') ;



      

      /*@=usereleased@*/ /* Splint limitation: shouldn't need these */

      --rel_buf_p;



      if (osd_isConnectChar (*(rel_buf_p-1)))

        *--rel_buf_p = '\0';



      /* fprintf (stderr, "Returning buffer: %s [%p]\n", rel_buffer, rel_buffer); */

      return rel_buffer;

    }

  /*@noaccess cstring@*/

# else

  return cstring_copy (filename);

# endif

}



cstring osd_getCurrentDirectory ()

{

# if defined(MSDOS) || defined(OS2)

  return cstring_makeLiteralTemp ("");

# else

  return cstring_makeLiteralTemp ("./");

# endif

}







